﻿module Math

open Microsoft.Xna.Framework
open Microsoft.FSharp.Core.LanguagePrimitives 

type Vector2< [< Measure >] 'u > =
  struct
    val mutable internal vector : Microsoft.Xna.Framework.Vector2

    member this.X 
      with get() = (this.vector.X |> Float32WithMeasure) : float32<'u>
      and set (v:float32<'u>) = this.vector.X <- v |> float32
    member this.Y 
      with get() = (this.vector.Y |> Float32WithMeasure) : float32<'u>
      and set (v:float32<'u>) = this.vector.Y <- v |> float32

    override this.ToString() = this.vector.ToString()

    new(x: float32<'u>, y: float32<'u>) = { vector = Microsoft.Xna.Framework.Vector2(x |> float32, y |> float32) }
    new(v: float32<'u>) = { vector = Microsoft.Xna.Framework.Vector2(v |> float32, v |> float32) }
    private new(v: Microsoft.Xna.Framework.Vector2) = { vector = v }

    static member Zero with get() = Vector2<'u>(Vector2.Zero)
    static member One with get() = Vector2<'u>(Vector2.One)
    static member UnitX with get() = Vector2<'u>(Vector2.UnitX)
    static member UnitY with get() = Vector2<'u>(Vector2.UnitY)

    static member (+) (v1:Vector2<'u>, v2:Vector2<'u>) = Vector2<'u>(v1.vector + v2.vector)
    static member (+) (v1:Vector2<'u>, f:float32<'u>) = Vector2<'u>(v1.X + f, v1.Y + f)
    static member (-) (v1:Vector2<'u>, v2:Vector2<'u>) = Vector2<'u>(v1.vector - v2.vector)
    static member (-) (v1:Vector2<'u>, f:float32<'u2>) = Vector2<'u>(v1.X - f, v1.Y - f)
    static member (*) (v1:Vector2<'u>, v2:Vector2<'u2>) = Vector2<'u * 'u2>(v1.X * v2.X, v1.Y * v2.Y)
    static member (*) (v1:Vector2<'u>, f:float32<'u2>) = Vector2<'u * 'u2>(v1.vector * (float32 f))
    static member (*) (f:float32<'u2>, v1:Vector2<'u>) = Vector2<'u * 'u2>(v1.vector * (float32 f))
    static member (/) (v1:Vector2<'u>, f:float32<'u2>) = Vector2<'u/'u2>(v1.vector / (float32 f))
    static member (/) (v1:Vector2<'u>, v2:Vector2<'u2>) = Vector2<'u/'u2>(v1.X / v2.X, v1.Y / v2.Y)
    static member (~-) (v:Vector2<'u>) = Vector2<'u>(-v.vector)

    static member inline private lift2 (f:Microsoft.Xna.Framework.Vector2*Microsoft.Xna.Framework.Vector2->'a) (v1:Vector2<'u>, v2:Vector2<'u>) = f(v1.vector,v2.vector)
    static member Dot(v1,v2) = Vector2<'u>.lift2 (Microsoft.Xna.Framework.Vector2.Dot) (v1,v2) |> Float32WithMeasure : float32<'u * 'u>
    static member Distance (v1:Vector2<'u>, v2:Vector2<'u>) = Vector2<'u>.lift2 (Microsoft.Xna.Framework.Vector2.Distance) (v1,v2) |> Float32WithMeasure : float32<'u>
    static member DistanceSquared (v1:Vector2<'u>, v2:Vector2<'u>) = Vector2<'u>.lift2 (Microsoft.Xna.Framework.Vector2.DistanceSquared) (v1,v2) |> Float32WithMeasure : float32<'u * 'u>
    static member Min (m:Vector2<'u>, v:Vector2<'u>) = Vector2<'u>(min m.X v.X, min m.Y v.Y)
    static member Max (m:Vector2<'u>, v:Vector2<'u>) = Vector2<'u>(max m.X v.X, max m.Y v.Y)
    static member Clamp (lower:Vector2<'u>, upper:Vector2<'u>, v:Vector2<'u>) = 
      let clamp l u v = v |> max l |> min u
      Vector2<'u>(clamp lower.X upper.X v.X, clamp lower.Y upper.Y v.Y)

    member this.Length() = this.vector.Length() |> Float32WithMeasure : float32<'u>
    member this.LengthSquared() = this.vector.LengthSquared() |> Float32WithMeasure : float32<'u * 'u>
    static member Normalize(v:Vector2<'u>) = Vector2<1>(Vector2.Normalize v.vector)

    //Microsoft.Xna.Framework.Vector2.

      
  end